#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Oct  6 14:30:22 2021

@author: Juntong
"""
import operator, functools, collections


class BaseVector:
    """向量类的基础。请使用`VectorFactory`创建的实例。
    """
    _mathematics_operator = {"__add__": operator.add,
                             "__sub__": operator.sub,
                             "__div__": operator.truediv,
                             "__floordiv__": operator.floordiv}

    def __init__(self, *value):
        if not value:
            value = [.0 for _ in range(self.length)]  # length属性由工厂类添加
        elif len(value) == 1:
            # 如果传入值是一个可迭代对象,如 Vector3D(range(3))，适用于此情况
            value = list(value[0])
        if len(value) != self.length:
            # 长度检查
            raise ValueError(f"需要长度为{self.length}的值。")
        self.value = tuple(value)

    def __repr__(self):
        # 特点是自适应类型名称。
        return type(self).__name__ + format(self.value)

    def __iter__(self):
        yield from self.value

    def __eq__(self, o):
        # 比较是否为相同类型 且 每个值之差为零
        return isinstance(o, type(self)) and \
               all(a - b == 0 for a, b in zip(self.value, o.value))

    def __bool__(self):
        # 判断是否有非零值
        return any(i != 0 for i in self.value)

    def __abs__(self):
        return pow(sum(i ** 2 for i in self.value), 0.5)

    def __neg__(self):
        return type(self)(-i for i in self.value)

    def __mul__(self, o):
        cls = type(self)
        if isinstance(o, cls):
            return sum(a * b for a, b in zip(self.value, o.value))
        if isinstance(o, collections.abc.Iterable):
            return self * cls(o)
        return cls(i * o for i in self.value)

    def _math_oper(self, oper, o):
        cls = type(self)
        if isinstance(o, cls):
            it = [oper(a, b) for a, b in zip(self.value, o.value)]
            return cls(it)
        if isinstance(o, collections.abc.Iterable):
            # 对方为可迭代对象，则生成一个Vector对象。
            ls = list(o)
            if len(ls) != cls.length:
                raise TypeError(f"长度不适宜。长度 {len(ls)}，应为{cls.length}")
            return self._math_oper(oper, cls(o))
        return type(self)(oper(i, o) for i in self.value)

    def __getitem__(self, num):
        return self.value[num]


for name, oper in BaseVector._mathematics_operator.items():
    func = functools.partialmethod(BaseVector._math_oper, oper)
    setattr(BaseVector, name, func)


class VectorFactory:
    """
    向量工厂类。输入指定的长度（维度），返回构造的向量。
    """
    vectors = {}

    def __new__(cls, nd):
        # ---- 值判断
        if not isinstance(nd, int):
            raise TypeError("nd must be int, got", type(nd))
        if nd <= 1:
            raise ValueError("nd must larger than 1, got", nd)
        # ---- 如果生成过nd对应的对象，则返回。
        if nd in cls.vectors:
            return cls.vectors[nd]
        # 创建对象
        r = cls.make_new_vector(nd)
        cls.vectors[nd] = r
        return r

    def make_new_vector(nd):
        d = {}
        if nd <= 3:
            # 如果nd <= 3，则可以通过.x, .y 至 .z 取值。
            def get_value(n, obj):
                return obj.value[n]

            for i in range(nd):
                d[chr(120 + i)] = property(functools.partial(get_value, i))
        r = type(f"Vector{nd}D", (BaseVector,), d)
        r.length = nd
        r.__doc__ = f"{nd}维向量，由`VectorFactory`创建。"
        return r


Vector2D = VectorFactory(2)
Vector3D = VectorFactory(3)
